package com.viu.technology.chat;

import com.alibaba.fastjson.JSONObject;
import com.viu.technology.dto.ChatMessage;
import com.viu.technology.dto.IMMessage;
import com.viu.technology.service.message.MessageService;
import com.viu.technology.util.ChannelUtil;
import com.viu.technology.util.RabbitUtil;
import io.netty.channel.*;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.core.Queue;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@ChannelHandler.Sharable
@Component
public class ChatServerHandler extends ChannelInboundHandlerAdapter {

    private static Logger log = LoggerFactory.getLogger(ChatServerHandler.class);


    public static int maxSocketNum = 0;

    @Autowired
    private ChannelUtil channelUtil;

    @Autowired
    private RabbitUtil rabbitUtil;

    @Autowired
    private AmqpTemplate rabbitTemplate;

    @Autowired
    private Queue chatDurableQueue;

    @Autowired
    private MessageService messageService;


    /**
     * 当有新的客户端连接服务端时执行该事件，主要用于添加Channel至ChannelGroup中，方便之后发送消息及维护
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerAdded(ChannelHandlerContext ctx) throws Exception {
        log.info("有新的连接，当前连接数："+channelUtil.getChannelsSize());
        channelUtil.channelAdded(ctx.channel());
        log.info("id为："+ctx.channel().id()+"连接成功，当前连接数："+channelUtil.getChannelsSize());
        System.out.println("最大的连接数量为：——————  " + ++maxSocketNum);
    }


    /**
     * 当有客户端与服务端断开连接时执行该事件，主要用于维护客户端与服务端之间的连接
     * @param ctx
     * @throws Exception
     */
    @Override
    public void handlerRemoved(ChannelHandlerContext ctx) throws Exception {
        log.info("有连接正在断开，当前连接数："+channelUtil.getChannelsSize());
        channelUtil.channelReomved(ctx.channel());
        log.info("id为："+ctx.channel().id()+"断开成功，当前连接数："+channelUtil.getChannelsSize());
    }

    /**
     * 服务端读取到客户端发送的消息时执行
     * @param ctx
     * @param msg
     * @throws Exception
     */
    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
        log.info("开始读取客户端消息");
        Channel channel = ctx.channel();
        String body = (String) msg;
        JSONObject msgJson = JSONObject.parseObject(body);
        //将读取到的消息转换为IMMessage实体类
        IMMessage message = msgJson.parseObject(body, IMMessage.class);

        //判断消息类型，0为token认证，1为文本消息
        if (message.getType() == 0) {
            //验证token，并返回token携带的用户ID
            String userId = channelUtil.channelAuthentication(channel, (String) message.getData());
            if (null!=userId) {
                log.info("token 认证成功");
                channelUtil.sendMsg(channel, new IMMessage( "用户ID为："+userId+"的socket token认证成功", userId, 1));

                //认证成功后，需要拉取该用户未读消息
                messageService.getUnreadMessage(userId);


            } else {
                log.info("token 认证失败");
                channelUtil.sendMsg(channel, new IMMessage( "socket token认证失败", null, 1));
            }
        } else if (message.getType() == 1) {
            ChatMessage chatMessage = JSONObject.parseObject((String) msgJson.get("data"), ChatMessage.class);

            //检查该channel是否已认证
            if (channelUtil.checkChannelAuthentication(chatMessage.getSenderId(), channel)) {
                try {
                    //后续可能会补充好友功能
                    //将消息发送至channel中，如果发送失败则存储到队列中
                    boolean b = channelUtil.sendMsg(chatMessage.getRecipientId(), msgJson);
                    if (b) {
                        log.info("ID为：" + chatMessage.getMessageId() + "的消息发送成功，转存至持久化队列中");
                        //由于接收人在线，所以直接将接收时间保存
                        chatMessage.setReceiptDate(System.currentTimeMillis());
                        msgJson.put("data", chatMessage.toString());
                        rabbitTemplate.convertAndSend(chatDurableQueue.getName(), msgJson.toString());
                    } else {
                        log.info("ID为：" + chatMessage.getMessageId() + "的消息发送失败，转存至未读化队列中");
                        rabbitUtil.declareQueueAndSendMsg("queue.chat.unread." + chatMessage.getRecipientId(), msgJson.toString());
                    }

                    //需要对发送人ack
                    channelUtil.sendMsg(channel, new IMMessage("消息发送成功", chatMessage.getMessageId(), 2));

                } catch (Exception e) {
                    //需要对发送人nack
                    channelUtil.sendMsg(channel, new IMMessage("消息发送失败，请重新发送", chatMessage.getMessageId(), 3));
                    throw e;
                }

            } else {
                channelUtil.sendMsg(channel, new IMMessage("Socket连接认证异常", chatMessage.getMessageId(), 1));
            }


        }



        //调用write方法异步发送应答消息给客户端
        //ChannelFuture future=ctx.writeAndFlush(echo);
        //服务端认为客户端接收到消息后主动关闭连接,短连接使用
        //future.addListener(ChannelFutureListener.CLOSE);*/
    }

    @Override
    public void channelReadComplete(ChannelHandlerContext ctx) throws Exception {
        log.info("读取客户端消息完成");
        //将消息发送队列中的消息写入到SocketChannel中发送给对方
        ctx.flush();
    }




    @Override
    public void exceptionCaught (ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //发生异常时，关闭ChannelHandleContext和释放相关句柄的资源
        log.info("消息发送异常");
        channelUtil.sendMsg(ctx.channel(), new IMMessage("发送异常", cause.getMessage(), 4));
        //ctx.close();
        cause.printStackTrace();
    }
}